package com.only4play.common.utils;

import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * Aes加密工具类
 *
 * @author liyuncong
 * @date 2023/11/15 14:32
 **/
public class AesUtils {


    private static final String KEY_ALGORITHM = "AES";

    /**
     * 默认的加密算法
     */
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";

    /**
     * 默认初始化向量
     */
    private static final String DEFAULT_IV = "#F%!D1Rs63W*_$v5";

    /**
     * 默认密钥
     */
    private static final String DEFAULT_SECRET_KEY = "5&2g4#D*_c0)J3!e";

    /**
     * 加密
     *
     * @param content 需要加密的内容
     * @return 加密后密文
     * @throws Exception 加密异常
     */
    public static String encrypt(String content) throws Exception {
        return encrypt(DEFAULT_IV, content, DEFAULT_SECRET_KEY);
    }

    /**
     * 加密
     *
     * @param content 需要加密的内容
     * @param key     签名密钥
     * @return 加密后密文
     * @throws Exception 加密异常
     */
    public static String encrypt(String content, String key) throws Exception {
        return encrypt(DEFAULT_IV, content, key);
    }

    /**
     * 加密
     *
     * @param iv      初始化向量
     * @param content 需要加密的内容
     * @param key     签名密钥
     * @return 加密后密文
     * @throws Exception 加密异常
     */
    public static String encrypt(String iv, String content, String key) throws Exception {
        byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        byte[] ivBytes = iv.getBytes(StandardCharsets.UTF_8);
        byte[] encryptedBytes = aesEncryptBytes(ivBytes, contentBytes, keyBytes);
        Base64.Encoder encoder = Base64.getEncoder();
        return encoder.encodeToString(encryptedBytes);
    }

    /**
     * 解密
     *
     * @param content 密文
     * @return 原文
     * @throws Exception 解密异常
     */
    public static String decrypt(String content) throws Exception {
        return decrypt(DEFAULT_IV, content, DEFAULT_SECRET_KEY);
    }

    /**
     * 解密
     *
     * @param content 密文
     * @param key     签名密钥
     * @return 原文
     * @throws Exception 解密异常
     */
    public static String decrypt(String content, String key) throws Exception {
        return decrypt(DEFAULT_IV, content, key);
    }

    /**
     * 解密
     *
     * @param iv      初始化向量
     * @param content 密文
     * @param key     签名密钥
     * @return 原文
     * @throws Exception 解密异常
     */
    public static String decrypt(String iv, String content, String key) throws Exception {
        Base64.Decoder decoder = Base64.getDecoder();
        byte[] encryptedBytes = decoder.decode(content);
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        byte[] ivBytes = iv.getBytes(StandardCharsets.UTF_8);
        byte[] decryptedBytes = aesDecryptBytes(ivBytes, encryptedBytes, keyBytes);
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }

    public static byte[] aesEncryptBytes(byte[] iv, byte[] contentBytes, byte[] keyBytes) throws Exception {
        return cipherOperation(iv, contentBytes, keyBytes, Cipher.ENCRYPT_MODE);
    }

    public static byte[] aesDecryptBytes(byte[] iv, byte[] contentBytes, byte[] keyBytes) throws Exception {
        return cipherOperation(iv, contentBytes, keyBytes, Cipher.DECRYPT_MODE);
    }

    /**
     * 加解密操作
     *
     * @param iv           初始化向量
     * @param contentBytes 原文字节数组
     * @param keyBytes     签名密钥字节数组
     * @param mode         加解密操作模式
     * @return 操作结果
     * @throws Exception 加解密操作异常
     */
    private static byte[] cipherOperation(byte[] iv, byte[] contentBytes, byte[] keyBytes, int mode) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
        cipher.init(mode, secretKey, ivParameterSpec);
        return cipher.doFinal(contentBytes);
    }

}
